package com.efivestar.idm.api.service.lock.protocol.handler;

import com.efivestar.idm.api.common.Cons;
import com.efivestar.idm.api.config.SpringContextHelper;
import com.efivestar.idm.api.domain.DeviceDomain;
import com.efivestar.idm.api.exception.ErrorParamException;
import com.efivestar.idm.api.repository.DeviceMapper;
import com.efivestar.idm.api.util.ByteUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.Date;

/**
 * 配网
 */
public abstract class AbstractLockProtocolHandler implements LockProtocolHanderlIntf {

    private static Logger logger = LoggerFactory.getLogger(AbstractLockProtocolHandler.class);
    /**
     * 保存数据的起始位置.
     */
    public static final int START_DATA_INDEX = 9;

    /**
     * 校验和所占用额字节数.
     */
    public static final int CHECK_BYTE_COUNT = 2;

    /**
     * 应打包的包标志.
     */
    public static final String RETURN_PACKET_DENTIFICATION = "07";

    @Transactional
    @Override
    public byte[] execute(ByteBuf byteBuf) {
        byteBuf.readerIndex(0);
        int totalLength = byteBuf.readableBytes();
        byte actualBts[] = new byte[totalLength];
        byteBuf.getBytes(0, actualBts, 0, totalLength);
        logger.info("接收的指令包:" + ByteUtils.bytes2hex(actualBts));
        //获取实际字节.
        ByteBuf actualByteBuf = byteBuf;
        actualByteBuf.readerIndex(0);
        // 填充包头和地址
        //返回信息时需要把下面的信息带上，返回给客户端
        //包头
        byte[] head = new byte[2];
        //地址
        byte[] address = new byte[4];
        //填充应答包的相关信息
        actualByteBuf.getBytes(0, head, 0, 2);
        actualByteBuf.getBytes(2, address, 0, 4);
        try {
            //获取数据包的长度
            int dataLength = actualByteBuf.skipBytes(7).readUnsignedShort() - CHECK_BYTE_COUNT;
            byte data[] = new byte[dataLength];
            actualByteBuf.getBytes(9, data, 0, dataLength);
            //将bytebuf重置read索引到第0位
            actualByteBuf.readerIndex(0);
            //跳过前6个字节
            actualByteBuf.skipBytes(6);
            //包标志数字
            int sum = 0;
            short dentificatoinNum = actualByteBuf.readUnsignedByte();
            sum += dentificatoinNum;

            //获取收到包的校验和
            for (int i = 0; i < dataLength + CHECK_BYTE_COUNT; i++) {
                sum += actualByteBuf.readUnsignedByte();
            }
            //获取包的实际校验和
            int actualSum = actualByteBuf.readUnsignedShort();
            // 校验两个数值的校验和不相等
            byte returnBts[] = null;
            if (actualSum != sum) {
                logger.error("指令包校验和错误:" + ByteUtils.bytes2hex(actualBts));
                returnBts = getCheckErrorBts(head, address);
            } else {
                //校验通过，交给具体的实现类去业务逻辑.
                //buf中保存的是 报内容
                ByteBuf buf = Unpooled.copiedBuffer(data);
                buf.readerIndex(0);

                // 读取数据包里面的imei和iccid
                ByteBuf tempBuf = Unpooled.copiedBuffer(data);
                tempBuf.readerIndex(0);
                int deviceIdLength = dataLength - getDataLenthWithoutDeviceId();
                byte lockIdBts[] = new byte[deviceIdLength];
                buf.getBytes(getDataLenthWithoutDeviceId(), lockIdBts, 0, deviceIdLength);
                String lockPackId = new String(lockIdBts);
                String lockId = "", iccid = "";
                if (!StringUtils.isEmpty(lockPackId)) {
                    String[] ids = lockPackId.split(",");
                    lockId = ids[0];
                    if (ids.length > 1) {
                        iccid = ids[1];
                    }
                }
                addOrUpdateDeviceOnNotFound(head, lockId, iccid);
                byte[] result = handleData(buf, lockId);
                //获取返回包的包内容长度
                int packetLenth = result.length + CHECK_BYTE_COUNT;
                byte[] lengthPacket = ByteUtils.intToBytes(packetLenth);
                byte[] dentification = ByteUtils.hexToBytes(RETURN_PACKET_DENTIFICATION);
                //返回给指纹锁的信息
                returnBts = ByteUtils.byteArrayConcat(head, address, dentification, lengthPacket, result, getCheckSum(dentification, lengthPacket, result));
            }
            return returnBts;
        } catch (ErrorParamException ex) {
            if (ex instanceof ErrorParamException) {
                //指令包字段内参数错误
                logger.error("指令包参数错误:" + ByteUtils.bytes2hex(actualBts));
                return getFieldErrorBts(head, address);
            }
        } catch (Exception ex) {
            throw ex;
        }
        return null;
    }

    /**
     * 获取报内容中去掉设备id之后的长度,从指令码开始，不包含设备id和校验和.
     *
     * @return
     */
    protected abstract int getDataLenthWithoutDeviceId();


    /**
     * 根据包标识、包长度、包内容生成校验码
     *
     * @return
     */
    private static byte[] getCheckSum(byte[] detification, byte[] lengthPacket, byte[] data) {
        ByteBuf detificationBBF = Unpooled.copiedBuffer(detification);
        ByteBuf lengthPcketnBBF = Unpooled.copiedBuffer(lengthPacket);
        ByteBuf dataPacketnBBF = Unpooled.copiedBuffer(data);
        int sum = 0;
        for (int i = 0; i < detification.length; i++) {
            sum += detificationBBF.readUnsignedByte();
        }
        for (int i = 0; i < lengthPacket.length; i++) {
            sum += lengthPcketnBBF.readUnsignedByte();
        }
        for (int i = 0; i < data.length; i++) {
            sum += dataPacketnBBF.readUnsignedByte();
        }
        return ByteUtils.intToBytes(sum);
    }


    /**
     * 校验失败返回的字节.
     *
     * @return
     */
    private byte[] getCheckErrorBts(byte[] head, byte[] address) {
        byte bts[] = new byte[12];

        ByteBuf byteBuf = Unpooled.copiedBuffer(bts);
        byteBuf.writerIndex(0);
        byteBuf.writeBytes(head);
        byteBuf.writeBytes(address);
        byteBuf.writeBytes(ByteUtils.hexToBytes("07"));
        byteBuf.writeBytes(ByteUtils.hexToBytes("0003"));
        byteBuf.writeBytes(ByteUtils.hexToBytes("01"));
        byteBuf.readerIndex(0).skipBytes(6);
        int sum = 0;
        for (int i = 0; i < 4; i++) {
            sum += byteBuf.readUnsignedByte();
        }
        byteBuf.writeShort(sum);
        byteBuf.getBytes(0, bts);
        return bts;
    }


    /**
     * 校验失败返回的字节.
     *
     * @return
     */
    private byte[] getFieldErrorBts(byte[] head, byte[] address) {
        byte bts[] = new byte[12];

        ByteBuf byteBuf = Unpooled.copiedBuffer(bts);
        byteBuf.writerIndex(0);
        byteBuf.writeBytes(head);
        byteBuf.writeBytes(address);
        byteBuf.writeBytes(ByteUtils.hexToBytes("07"));
        byteBuf.writeBytes(ByteUtils.hexToBytes("0003"));
        byteBuf.writeBytes(ByteUtils.hexToBytes("F0"));
        byteBuf.readerIndex(0).skipBytes(6);
        int sum = 0;
        for (int i = 0; i < 4; i++) {
            sum += byteBuf.readUnsignedByte();
        }
        byteBuf.writeShort(sum);
        byteBuf.getBytes(0, bts);
        return bts;
    }

    @Override
    public boolean match(byte[] head, byte[] command) {
        byte[] actualHead = ByteUtils.hexToBytes(udpHead());
        byte[] actualCommand = ByteUtils.hexToBytes(udpCommand());
        return (Arrays.equals(head, actualHead) && Arrays.equals(command, actualCommand));
    }


    /**
     * 当设备不存在时，先插入设备.
     * 当设备存在时，更新心跳时间.
     *
     * @param head
     * @param deviceId
     * @param iccid
     */
    protected void addOrUpdateDeviceOnNotFound(byte[] head, String deviceId, String iccid) {
        DeviceDomain deviceDomain = DeviceDomain.builder().deviceId(deviceId).iccid(iccid).type(Cons.LOCK).createTime(new Date()).heartTime(new Date()).protocolHeader(ByteUtils.bytes2hex(head)).build();
        DeviceMapper deviceMapper = SpringContextHelper.getBean(DeviceMapper.class);
        DeviceDomain dbDevice = deviceMapper.selectByPrimaryKey(deviceId);
        if (dbDevice == null) {
            deviceDomain.setDefenseStatus(Cons.DEFENSE_OFF_STATUS);
            deviceMapper.insert(deviceDomain);
        } else {
            dbDevice.setHeartTime(new Date());
            dbDevice.setIccid(iccid);
            dbDevice.setProtocolHeader(ByteUtils.bytes2hex(head));
            deviceMapper.updateByPrimaryKey(dbDevice);
        }
    }

    /**
     * 处理协议中的数据
     *
     * @param buf
     * @param lockId
     * @return
     */
    public abstract byte[] handleData(ByteBuf buf, String lockId);

    public byte[] handleData(ByteBuf buf, String lockId, int sequence) {
        return ByteUtils.hexToBytes("00");
    }


    /**
     * 数据包的包头.
     *
     * @return
     */
    protected abstract String udpHead();

    /**
     * 数据包的指令码.
     *
     * @return
     */
    protected abstract String udpCommand();


    public static void main(String[] args) {
        System.out.println(ByteUtils.bytes2hex(getCheckSum(ByteUtils.hexToBytes("01"), ByteUtils.hexToBytes("16"), ByteUtils.hexToBytes("c319000000313131313131313131313333333333"))));
        System.out.println(Integer.parseUnsignedInt("18", 16));
    }


}
